﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Tippelde.Entities;
using Tippelde.Services.Dtos;

namespace Tippelde.Services.Groups
{
    public class GroupManager : IGroupManager
    {
        private readonly Expression<Func<Group, GroupHeaderData>> GroupHeaderSelector =
            g => new GroupHeaderData
                {
                    Id = g.Id,
                    Name = g.Name,
                    ShortName = g.ShortName,
                    Description = g.Description
                };


        public IEnumerable<GroupHeaderData> GetAllGroupHeaders()
        {
            using (var ctx = new TippeldeEntities())
            {
                return ctx.Groups.Select(GroupHeaderSelector).ToList();
            }
        }

        public Group CreateGroup(string creatorName, string name, string shortName, string description)
        {
            using (var ctx = new TippeldeEntities())
            {
                User creator = ctx.Users.Include(u => u.UserGroups).SingleOrDefault(u => u.UserName == creatorName);
                if (creator == null)
                    return null;
                Group g = new Group
                {
                    Name = name,
                    ShortName = shortName,
                    Description = description,
                    Creator = creator
                };
                ctx.Groups.Add(g);
                UserGroup ug = new UserGroup
                {
                    User = creator,
                    Group = g
                };
                creator.UserGroups.Add(ug);
                g.UserGroups.Add(ug);
                ctx.SaveChanges();
                return g;
            }
        }

        public Group EditGroup(GroupHeaderData group)
        {
            using (var ctx = new TippeldeEntities())
            {
                Group g = ctx.Groups.Single(gg => gg.Id == group.Id);
                g.Name = group.Name;
                g.ShortName = group.ShortName;
                g.Description = group.Description;
                ctx.SaveChanges();
                return g;
            }
        }

        public List<int> GetGroupIdsForUser(string userName)
        {
            using (var ctx = new TippeldeEntities())
            {
                return ctx.UserGroups.Where(u => u.User.UserName == userName).Select(ug => ug.GroupId).ToList();
            }
        }

        public List<int> GetGroupIdsForEditor(string userName)
        {
            using (var ctx = new TippeldeEntities())
            {
                return ctx.UserGroups.Where(ug => ug.IsEditor && ug.User.UserName == userName).Select(ug => ug.GroupId).ToList();
            }
        }

        public List<string> GetUserIdsForGroup(int groupId)
        {
            using (var ctx = new TippeldeEntities())
            {
                return ctx.UserGroups.Where(ug => ug.GroupId == groupId).Select(u => u.UserId).ToList();
            }
        }
        public List<string> GetEditorUserIdsForGroup(int groupId)
        {
            using (var ctx = new TippeldeEntities())
            {
                return ctx.UserGroups.Where(ug => ug.GroupId == groupId && ug.IsEditor).Select(u => u.UserId).ToList();
            }
        }

        public async Task AssignUserToGroupsAsync(string userId, Dictionary<int, bool> groupIdsAssigned)
        {
            using (var ctx = new TippeldeEntities())
            {
                var user = ctx.Users.Include(u => u.UserGroups).SingleOrDefault(u => u.Id == userId);
                if (user != null)
                {
                    foreach (Group g in ctx.Groups.ToList())
                    {
                        bool needed = groupIdsAssigned[g.Id];
                        try
                        {
                            bool isGroupContainedByUser = g.UserGroups.Any(ug => user.Id == ug.UserId);
                            if (needed)
                            {
                                if (!isGroupContainedByUser)
                                {

                                    UserGroup ug = new UserGroup
                                    {
                                        User = user,
                                        Group = g
                                    };
                                    g.UserGroups.Add(ug);
                                    user.UserGroups.Add(ug);
                                    ctx.UserGroups.Add(ug);
                                }
                            }
                            else
                            {
                                if (isGroupContainedByUser)
                                {
                                    UserGroup ug = ctx.UserGroups.SingleOrDefault(p => p.UserId == user.Id && p.GroupId == g.Id);
                                    if (ug != null)
                                    {
                                        g.UserGroups.Remove(ug);
                                        user.UserGroups.Remove(ug);
                                        ctx.UserGroups.Remove(ug);
                                    }
                                }
                            }
                        }
                        catch (KeyNotFoundException) { }

                    }
                    await ctx.SaveChangesAsync();
                }
            }
        }

        public async Task AssignToGroupAsync(int groupId, Dictionary<string, bool[]> userIdsAssigned)
        {
            using (var ctx = new TippeldeEntities())
            {
                var group = ctx.Groups.Include(g => g.UserGroups).SingleOrDefault(g => g.Id == groupId);
                if (group != null)
                {
                    foreach (User u in ctx.Users.ToList())
                    {
                        bool needed = userIdsAssigned[u.Id][0];
                        bool isEditor = userIdsAssigned[u.Id][1];
                        try
                        {
                            bool isUserContainedInAGroup = u.UserGroups.Any(ug => ug.GroupId == group.Id);
                            if (needed)
                            {
                                if (!isUserContainedInAGroup)
                                {
                                    UserGroup ug = new UserGroup
                                    {
                                        User = u,
                                        Group = group,
                                        IsEditor = isEditor
                                    };
                                    u.UserGroups.Add(ug);
                                    group.UserGroups.Add(ug);
                                    ctx.UserGroups.Add(ug);
                                }
                                else
                                {
                                    UserGroup userGroup = u.UserGroups.SingleOrDefault(ug => ug.GroupId == group.Id);
                                    userGroup.IsEditor = isEditor;
                                }

                            }
                            else
                            {
                                if (isUserContainedInAGroup)
                                {
                                    UserGroup ug = ctx.UserGroups.SingleOrDefault(p => p.UserId == u.Id && p.GroupId == group.Id);
                                    if (ug != null)
                                    {
                                        u.UserGroups.Remove(ug);
                                        group.UserGroups.Remove(ug);
                                        ctx.UserGroups.Remove(ug);
                                    }
                                }
                            }
                        }
                        catch (KeyNotFoundException) { }

                    }
                    await ctx.SaveChangesAsync();
                }
            }
        }

        public GroupHeaderData GetGroupHeaderById(int groupId)
        {
            using (var ctx = new TippeldeEntities())
            {
                return ctx.Groups.Where(g => g.Id == groupId).Select(GroupHeaderSelector).SingleOrDefault();
            }
        }


        public IEnumerable<GroupHeaderData> GetGroupHeadersForUser(string userName)
        {
            using (var ctx = new TippeldeEntities())
            {
                return ctx.Groups.Where(g => g.UserGroups.Any(u => u.User.UserName == userName)).Select(GroupHeaderSelector).ToList();
            }
        }

        public List<int> GetActiveGroupIdsForEvent(int eventId)
        {
            try
            {
                using (var ctx = new TippeldeEntities())
                {
                    var e = ctx.Events.Single(e_ => e_.Id == eventId);
                    return e.Groups.Select(g => g.Id).ToList();
                }
            }
            catch (Exception)
            {
                return new List<int>();
            }
        }

        public List<int> GetActiveGroupIdsForNews(int newsId)
        {
            try
            {
                using (var ctx = new TippeldeEntities())
                {
                    var news = ctx.News.Single(n => n.Id == newsId);
                    return news.Groups.Select(g => g.Id).ToList();
                }
            }
            catch (Exception)
            {
                return new List<int>();
            }
        }

        public bool HasAnyEditorRights(string userName)
        {
            using (var ctx = new TippeldeEntities())
            {
                return ctx.UserGroups.Where(ug => ug.User.UserName == userName).Any(ug => ug.IsEditor);
            }
        }

        public bool HasAnyEditorRightsForGroup(string userName, int groupId)
        {
            using (var ctx = new TippeldeEntities())
            {
                return ctx.UserGroups.Where(ug => ug.User.UserName == userName && ug.GroupId == groupId).Any(ug => ug.IsEditor);
            }
        }

        public bool HasAnyEditorRightsForEvent(string userName, int eventId)
        {
            using (var ctx = new TippeldeEntities())
            {
                return ctx.Events.Where(e => e.Id == eventId).SelectMany(e => e.Groups).SelectMany(g => g.UserGroups).Where(ug => ug.User.UserName == userName).Any(ug => ug.IsEditor);
            }
        }
        public bool HasAnyEditorRightsForNews(string userName, int newsId)
        {
            using (var ctx = new TippeldeEntities())
            {
                return ctx.News.Where(n => n.Id == newsId).SelectMany(n => n.Groups).SelectMany(g => g.UserGroups).Where(ug => ug.User.UserName == userName).Any(ug => ug.IsEditor);
            }
        }

        public void DeleteGroup(int id)
        {
            using (var ctx = new TippeldeEntities())
            {
                var userGroups = ctx.UserGroups.Where(ug => ug.GroupId == id).ToList();
                var group = ctx.Groups.Single(g => g.Id == id);
                var events = ctx.Events.Where(e => e.Groups.Any(g =>g.Id == group.Id)).ToList();
                events.ForEach(e => e.Groups.Remove(group));

                var news = ctx.News.Where(n => n.Groups.Any(g => g.Id == group.Id)).ToList();
                news.ForEach(n => n.Groups.Remove(group));
                userGroups.ForEach(ug => ctx.UserGroups.Remove(ug));
                ctx.Groups.Remove(group);
                ctx.SaveChanges();
            }
        }
    }
}
